home *** CD-ROM | disk | FTP | other *** search
- /* flock.c : flock() and amiga_flock()
-
- * flock()
-
- * Attempts to emulate flock().
- * Lock files (filename.lock) are used for this.
- * open() does not support bloking behaviour, so if non-blocking behaviour
- * is not requested, an attempt will be made every second until locking
- * is successful.
- * ChangeMode() does not support blocking behaviour, so if non-blocking
- * behaviour is not requested, an attempt will be made every second until
- * locking is successful.
- */
-
- #define _KERNEL
- #define _INTERNAL_FILE
-
- #include <dos/dos.h>
- #include <dos/dosextens.h>
-
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/file.h>
- #include <errno.h>
-
- #include <proto/dos.h>
-
- static fd_set locked;
- static fd_set shared;
- static int lock_fd [FD_SETSIZE];
-
- int amiga_flock(int fd, int flags);
- static int create_lock (int fd, BOOL exclusive, BOOL blocking);
- static int create_lock_file (char *lockname, BOOL exclusive);
-
- int flock (int fd, int flags)
- {
- /* Create a new shared lock or turn an existing exclusive lock into
- * a shared one.
- */
- if (flags & LOCK_SH)
- {
- /* Change existing shared lock. */
- if (FD_ISSET (fd, &locked))
- {
- if (FD_ISSET (fd, &shared))
- return 0;
- else
- {
- if (-1 != amiga_flock (lock_fd [fd], ((flags & LOCK_NB) | LOCK_SH)))
- {
- FD_SET (fd, &shared);
- return (0);
- }
- else
- return (-1);
- }
- }
- /* Create new shared lock. */
- else
- {
- if (-1 != (lock_fd [fd] = create_lock (fd, FALSE, !(flags & LOCK_NB))))
- {
- FD_SET (fd, &locked);
- FD_SET (fd, &shared);
- return (0);
- }
- else
- return (-1);
- }
- }
-
- /* Create a new exclusive lock or turn an existing shared lock into
- * an exclusive one.
- */
- if (flags & LOCK_EX)
- {
- /* Change existing shared lock. */
- if (FD_ISSET (fd, &locked))
- {
- if (! FD_ISSET (fd, &shared))
- return 0;
- else
- {
- if (-1 != amiga_flock (lock_fd [fd], ((flags & LOCK_NB) | LOCK_EX)))
- {
- FD_CLR (fd, &shared);
- return (0);
- }
- else
- return (-1);
- }
- }
- /* Create new exclusive lock. */
- else
- {
- if (-1 != (lock_fd [fd] = create_lock (fd, TRUE, !(flags & LOCK_NB))))
- {
- FD_SET (fd, &locked);
- FD_CLR (fd, &shared);
- return (0);
- }
- else
- return (-1);
- }
- }
-
- /* Release an existing lock. */
- if (flags & LOCK_UN)
- {
- if (FD_ISSET (fd, &locked))
- {
- close (lock_fd [fd]);
- FD_CLR (fd, &locked);
- }
- return (0);
- }
-
- /* No valid flags specified? */
- errno = EINVAL;
- return (-1);
- }
-
- static int create_lock (int fd, BOOL exclusive, BOOL blocking)
- {
- struct file *fp;
- char *lockname;
- int lock_fd;
-
- fp = (struct file *) fcntl (fd, F_EXTERNALIZE, 0);
- if (fp == (struct file *) -1 || fp == NULL)
- return (-1);
-
- if (! (( lockname = malloc (strlen (fp->f_name) + 6) )) )
- {
- errno = ENOMEM;
- return (-1);
- }
-
- strcpy (lockname, fp->f_name);
- strcat (lockname, ".lock");
- errno = 0;
-
- lock_fd = create_lock_file (lockname, exclusive);
-
- /* If the lock failed: exit with error if non-blocking, or keep
- * retrying until success (or unrelated failure) if blocking.
- */
- while (errno == EWOULDBLOCK && blocking)
- {
- errno = 0;
- sleep (1);
- lock_fd = create_lock_file (lockname, exclusive);
- }
-
- return (lock_fd);
- }
-
- static int create_lock_file (char *lockname, BOOL exclusive)
- {
- struct file *lockfile;
- int temp_errno;
- int lock_fd;
-
- /* Attempt to open the lock file. To avoid deadlock (multiple tasks
- * managing to open() before at least one amiga_lock()s) on exclusive
- * locks, add O_EXCL when exclusive mode is requested.
- */
- if (-1 == (lock_fd = open (lockname, O_CREAT /* | (exclusive ? O_EXCL : 0) */ )))
- {
- if (errno == EEXIST)
- errno = EWOULDBLOCK;
- return (-1);
- }
-
- /* The lock file should be deleted when it is closed. */
- lockfile = (struct file *) fcntl (lock_fd, F_EXTERNALIZE);
- if (lockfile == (struct file *) -1 || lockfile == NULL)
- {
- temp_errno = errno;
- close (lock_fd);
- errno = temp_errno;
- return (-1);
- }
- lockfile->f_flags |= FUNLINK;
-
- /* Change file handle to exclusive mode if requested. */
- if (exclusive && !errno)
- {
- if (-1 == (amiga_flock (lock_fd, LOCK_EX | LOCK_NB)))
- {
- temp_errno = errno;
- close (lock_fd);
- errno = temp_errno;
- return (-1);
- }
- }
- return (lock_fd);
- }
-
- /* amiga_flock()
-
- * Similar to flock(), but uses mandatory Amiga file locks instead of
- * advisory locks.
- * Note that because of this, there will always be at least a shared lock
- * on the file with this implementation.
- * ChangeMode() does not support blocking behaviour, so if non-blocking
- * behaviour is not requested, an attempt will be made every second until
- * locking is successful.
- */
-
- /*
- #define DEBUG_VERSION
- #include <kprintf.h>
-
- char __flock_errorbuf[256];
- char __flock_fnamebuf[1024];
- char __flock_tnamebuf[1024];
- */
-
- /* FIXME ix_amiga.h doesn't declare this function. */
- /* #include <ix_amiga.h> */
- BPTR __amiga_filehandle (int fd);
-
- int amiga_flock (int fd, int flags)
- {
- BPTR filehandle;
-
- /* FIXME __amiga_filehandle() doesn't always set errno on failure :-( */
- errno = 0;
-
- /* Get the filehandle (if any) associated with the file descriptor */
- if ((filehandle = __amiga_filehandle (fd)))
- {
- ULONG newmode;
- BOOL success;
-
- if ((flags & LOCK_SH) || (flags & LOCK_UN))
- newmode = SHARED_LOCK;
- else if (flags & LOCK_EX)
- newmode = EXCLUSIVE_LOCK;
- else
- return (0);
-
- if (flags & LOCK_NB)
- {
- if ((success = ChangeMode (CHANGE_FH, filehandle, newmode)))
- return (0);
- else
- {
- errno = EWOULDBLOCK;
- /*
- Fault (IoErr(), NULL, __flock_errorbuf, 256);
- NameFromFH (filehandle, __flock_fnamebuf, 1024);
- GetProgramName (__flock_tnamebuf, 1024);
- KPrintF ("%s: ChangeMode (%s,%ld) failure: %s\n",
- __flock_tnamebuf, __flock_fnamebuf , newmode,
- __flock_errorbuf);
- */
- return (-1);
- }
- }
- else
- /* Blocking behaviour - loop (with delay) until success. */
- {
- while (! ChangeMode (CHANGE_FH, filehandle, newmode))
- sleep (1);
- return (0);
- }
- }
- /* Getting the file handle failed. */
- else
- {
- if (errno != EBADF)
- errno = EINVAL;
- return (-1);
- }
- }
-